-- Copyright (c) 2024 Huawei Technologies Co., Ltd.
-- openUBMC is licensed under Mulan PSL v2.
-- You can use this software according to the terms and conditions of the Mulan PSL v2.
-- You may obtain a copy of Mulan PSL v2 at:
--         http://license.coscl.org.cn/MulanPSL2
-- THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
-- EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
-- MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
-- See the Mulan PSL v2 for more details.

local class = require 'mc.class'
local singleton = require 'mc.singleton'
local log = require 'mc.logging'
local mdb = require 'mc.mdb'

local CHASSIS_INLET_TEMP<const> = 'chassis.inlettemp'
local CHASSIS_OUTLET_TEMP<const> = 'chassis.outlettemp'

local FRUDATA_OVERVIEW_INTF<const> = 'bmc.kepler.Systems.FruData.Overview'
local FRUDATA_SYSTEM_INTF<const> = 'bmc.kepler.Systems.FruData.System'
local FRUDATA_BOARD_INTF<const> = 'bmc.kepler.Systems.FruData.Board'
local SENSOR_THRESHOLDSENSOR_INTF<const> = 'bmc.kepler.Systems.ThresholdSensor'
local SENSOR_THRESHOLDSENSOR_DISPLAY_INTF<const> = 'bmc.kepler.Systems.ThresholdSensorDisplay'

local metric_collect = class()

function metric_collect:ctor(bus)
    self.bus = bus
end

local function get_bmc_frudata_obj(bus)
    -- 获取fru对象
    local ok, frudata_objs = pcall(mdb.get_sub_objects, bus, '/bmc/kepler/Systems/1/FruDatas',
        FRUDATA_OVERVIEW_INTF)
    if not ok then
        return
    end

    return frudata_objs:fold(function(obj)
        if obj.FruName == 'BMC' then
            return obj, false
        end
    end)
end

function metric_collect:get_chassis_data_collection_items(obj)
    local ok, chassis = pcall(mdb.get_object, self.bus, obj.path, 'bmc.kepler.Chassis')
    if not ok then
        return '', {}, {}, {}
    end

    local bmc_frudata = get_bmc_frudata_obj(self.bus)
    if not bmc_frudata or not bmc_frudata.path then
        return '', {}, {}, {}
    end

    local ok, frudata_system = pcall(mdb.get_object, self.bus, bmc_frudata.path, FRUDATA_SYSTEM_INTF)
    if not ok then
        return '', {}, {}, {}
    end

    local ok, frudata_board = pcall(mdb.get_object, self.bus, bmc_frudata.path, FRUDATA_BOARD_INTF)
    if not ok then
        return '', {}, {}, {}
    end

    local Classification = {
        {PropName = 'SystemName', PropVal = frudata_system.SystemProductName},
        {PropName = 'ChassisId', PropVal = '1'}
    }
    local Identification = {
        {PropName = 'LocationInfo', PropVal = chassis.Location},
        {PropName = 'DeviceSerialNumber', PropVal = frudata_system.SystemSerialNumber},
        {PropName = 'MfgDateTime', PropVal = frudata_board.MfgDate}
    }
    local MetricName = {CHASSIS_INLET_TEMP, CHASSIS_OUTLET_TEMP}
    return 'Chassis', Classification, Identification, MetricName
end

local function get_sensor_obj_reading(bus, name)
    local ok, sensor_objs = pcall(mdb.get_sub_objects, bus,
        '/bmc/kepler/Systems/1/ThresholdSensors', SENSOR_THRESHOLDSENSOR_INTF)
    if not ok then
        return
    end

    local sensor_obj = sensor_objs:fold(function(obj)
        if obj.SensorIdentifier == name then
            return obj, false
        end
    end)

    if not sensor_obj or not sensor_obj.path then
        return
    end

    local ok, display = pcall(mdb.get_object, bus, sensor_obj.path,
        SENSOR_THRESHOLDSENSOR_DISPLAY_INTF)
    if not ok then
        return
    end

    return display.ReadingDisplay
end

local function get_inlet_temp(bus, data)
    local reading = get_sensor_obj_reading(bus, 'Inlet Temp')
    table.insert(data, reading)
end

local function get_outlet_temp(bus, data)
    local reading = get_sensor_obj_reading(bus, 'Outlet Temp')
    table.insert(data, reading)
end

local metric_func_table = {
    [CHASSIS_INLET_TEMP] = {metric_func = get_inlet_temp},
    [CHASSIS_OUTLET_TEMP] = {metric_func = get_outlet_temp}
}

local function get_metric_data(bus, metric_name)
    log:debug('metric_name = %s', metric_name)
    local data = {}
    if metric_func_table[metric_name] ~= nil then
        metric_func_table[metric_name].metric_func(bus, data)
    else
        log:info('invalid metric, metric_name = %s', metric_name)
    end
    return data
end

function metric_collect:get_chassis_data_collection_data(obj, metric_name)
    local Val = {}
    for _, value in ipairs(metric_name) do
        local data = get_metric_data(self.bus, value)
        if #data ~= 0 then
            table.insert(Val, {MetricName = value, Data = data})
        end
    end
    return Val
end

return singleton(metric_collect)
